package com.fbms.common.exception;

import com.fbms.common.result.Res;
import com.fbms.common.result.ResBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Optional;
import java.util.stream.Collectors;


/**
 * @author lan
 * @since 2023-03-15
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public Res<Object> exHandler(Exception e) {

        log.error("global exe:", e);

        if (e instanceof HttpRequestMethodNotSupportedException) {
            return handleHttpRequestMethodNotSupportedException((HttpRequestMethodNotSupportedException) e);
        }

        if (e instanceof HttpMessageNotReadableException) {
            return handleHttpMessageNotReadableException((HttpMessageNotReadableException) e);
        }

        if (e instanceof MissingServletRequestParameterException) {
            return handleMissingServletRequestParameterException((MissingServletRequestParameterException) e);
        }

        if (e instanceof MethodArgumentNotValidException) {
            return handleMethodArgumentNotValidException((MethodArgumentNotValidException) e);
        }

        if (e instanceof ConstraintViolationException) {
            return handleConstraintViolationException((ConstraintViolationException) e);
        }

        return ResBuilder.ofError(e.getMessage());
    }

    /**
     * 使用@Valid注解所抛出的异常（除了Controller中的接口中的@Valid）
     *
     * @param e 异常
     * @return 格式化结果
     */
    private Res<Object> handleConstraintViolationException(ConstraintViolationException e) {
        String errorMsg = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining(","));
        log.error(errorMsg, e);
        return ResBuilder.ofError(errorMsg);
    }

    private Res<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        String errorMsg = Optional
                .ofNullable(e.getBindingResult().getFieldError())
                .map(FieldError::getDefaultMessage)
                .orElse("请检查参数是否正确");
        log.error(errorMsg, (Throwable) e);
        return ResBuilder.ofError(errorMsg);
    }

    /**
     * 当get请求，没变传相应的参数的时候，会报此异常
     *
     * @param e 异常
     * @return 返回给前端的值
     */
    private Res<Object> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        return ResBuilder.ofError(e.getMessage());
    }

    private Res<Object> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        return ResBuilder.ofError("可能的原因为:json反序列化失败,请检查body参数");
    }

    /**
     * 当一个 接口的 请求 方法不对时，会报这个错，
     * 例如：一个接口设置的为Get请求，但是调用时，使用的为Post，则会抛出如下的异常
     *
     * @param e 异常
     * @return 返回结果
     */
    private Res<Object> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return ResBuilder.ofError("请检查请求方法是否正确【POST? GET? DELETE? PUT? Or Other】");
    }


}